# window.py
#
# Copyright 2019 RemixDevs
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
from gi.repository import Gtk, Handy, GLib, GdkPixbuf, Gio
from demixed.deezer import Deezer
import json
from deezer import Client
import threading
import requests
from multiprocessing import Process, Pipe
from mutagen.flac import FLAC, Picture
from mutagen import id3
from mutagen.id3 import ID3NoHeaderError
from mutagen.id3 import ID3, TIT2, TALB, TPE1, APIC, TRCK, TDRC, TPOS, TSRC, TCON
from mutagen.mp3 import MP3

Handy.init()

@Gtk.Template(resource_path='/sucks/copyright/demixedgtk/window.ui')
class DemixedgtkWindow(Gtk.ApplicationWindow):
    __gtype_name__ = 'DemixedgtkWindow'

    login_button = Gtk.Template.Child()
    preferences_revealer = Gtk.Template.Child()
    download_button = Gtk.Template.Child()
    password = Gtk.Template.Child()
    download_item_button = Gtk.Template.Child()
    stack = Gtk.Template.Child()
    downloadpage = Gtk.Template.Child()
    deezer_link = Gtk.Template.Child()
    error_login = Gtk.Template.Child()
    login_spinner = Gtk.Template.Child()
    spinner_revealer = Gtk.Template.Child()
    error_reveal = Gtk.Template.Child()
    track_name = Gtk.Template.Child()
    found_track = Gtk.Template.Child()
    picture = Gtk.Template.Child()
    search_or_link_switcher = Gtk.Template.Child()
    info_popover_revealer = Gtk.Template.Child()
    details_box = Gtk.Template.Child()
    deezer_find_spinner = Gtk.Template.Child()
    deezer_find_spinner_revealer = Gtk.Template.Child()
    FLAC = Gtk.Template.Child()
    MP3_320 = Gtk.Template.Child()
    MP3_128 = Gtk.Template.Child()
    GioApplication = Gio.Application.get_default
    about_button = Gtk.Template.Child()
    preferences_button = Gtk.Template.Child()
    albums_or_tracks = Gtk.Template.Child()
    albums_or_tracks_revealer = Gtk.Template.Child()
    stop_item_button = Gtk.Template.Child()
    stop_item_revealer = Gtk.Template.Child()
    demixed_schemas = Gio.Settings.new("sucks.copyright.demixedgtk")
    music_folder = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_MUSIC)
    music_quality = ["FLAC", "MP3_320", "MP3_128"]

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        eval("self."+str(self.music_quality[self.demixed_schemas.get_uint("defaultaudio")])+".set_active(True)")
        deezersecretcookie = self.demixed_schemas.get_string("deezercookie")
        if deezersecretcookie:
            self.password.set_text(deezersecretcookie)
            self.login_clicked(self.login_button)

    @Gtk.Template.Callback()
    def MP3_320_toggled(self, button):
        if self.MP3_320.get_active():
            self.demixed_schemas.set_uint("defaultaudio",1)

    @Gtk.Template.Callback()
    def MP3_128_toggled(self, button):
        if self.MP3_128.get_active():
            self.demixed_schemas.set_uint("defaultaudio",2)

    @Gtk.Template.Callback()
    def FLAC_toggled(self, button):
        if self.FLAC.get_active():
            self.demixed_schemas.set_uint("defaultaudio",0)

    @Gtk.Template.Callback()
    def album_tracks_activate(self, rows, row):
        if self.albums_or_tracks_revealer.get_reveal_child():
            self.albums_or_tracks_revealer.set_reveal_child(False)
        else:
            self.albums_or_tracks_revealer.set_reveal_child(True)

    @Gtk.Template.Callback()
    def stop_item_button_clicked(self, button):
        track = None
        real_album = None
        multiprocess_download.terminate()
        self.hide_download_spinner()
        self.download_item_button.set_sensitive(True)
        self.stop_item_revealer.set_reveal_child(False)


    @Gtk.Template.Callback()
    def about_button_click(self, button):
        about_dialog = Gtk.AboutDialog()
        about_dialog.set_logo(None)
        about_dialog.set_modal(True)
        about_dialog.set_authors(['owowhatsdeez', 'avysm'])
        about_dialog.set_license_type(Gtk.License.GPL_3_0)
        about_dialog.set_program_name(('Demixed'))
        about_dialog.set_comments(_('Music Downloader'))
        about_dialog.set_website('https://notabug.org/RemixDevs/demixed-gtk')
        about_dialog.set_transient_for(self)
        about_dialog.run()
        about_dialog.destroy()

    def threading_download_callback(self, not_error, media, media_type):
        self.stop_item_revealer.set_reveal_child(False)
        self.hide_download_spinner()
        self.download_item_button.set_sensitive(True)
        self.preferences_button.set_sensitive(True)
        if not not_error:
            download_track_error = Gio.Notification.new("Couldn't download")
            download_track_error.set_icon(Gio.Icon.new_for_string("face-sad-symbolic"))
            self.GioApplication().send_notification(None, download_track_error)
        else:
            download_track_successful = Gio.Notification.new(media_type+" downloaded!")
            download_track_successful.set_icon(Gio.Icon.new_for_string("folder-music-symbolic"))
            download_track_successful.set_body(media)
            self.GioApplication().send_notification(None, download_track_successful)

    def track_download(self, current_music_quality, current_track, is_single_track, album_art):
        try:
            music_filename = self.music_folder+"/"+current_track.guess_file_name()
            current_track.download(music_filename, current_music_quality)
            artist_list = []
            for artist_name in current_track.artists:
                artist_list.append(artist_name.name)
            genre = requests.get("https://api.deezer.com/album/88812882/?countryCode=us", headers={"Accept-Language": "en-US,en;q=0.5"}).json()['genres']['data'][0]['name']
            if self.FLAC.get_active():
                flacfile = FLAC(music_filename+".flac")
                flacfile['title'] = current_track.title
                flacfile['album'] = current_track.album.title
                flacfile['artist'] = artist_list
                flacfile['tracknumber'] = current_track.track_number
                flacfile['date'] = current_track.album.date
                flacfile['discnumber'] = current_track.disk_number
                flacfile['isrc'] = current_track.isrc
                flacfile['genre'] = genre
                picture = Picture()
                picture.data = album_art
                picture.type = id3.PictureType.COVER_FRONT
                picture.mime = u"image/jpeg"
                flacfile.add_picture(picture)
                flacfile.save()
            else:
                mp3file = MP3(music_filename+".mp3", ID3=ID3)
                mp3file["TIT2"] = TIT2(encoding=3, text=current_track.title)
                mp3file["TALB"] = TALB(encoding=3, text=current_track.album.title)
                mp3file["TPE1"] = TPE1(encoding=3, text=artist_list)
                mp3file["TRCK"] = TRCK(encoding=3, text=current_track.track_number)
                mp3file["TDRC"] = TDRC(encoding=3, text=current_track.album.date)
                mp3file["TPOS"] = TPOS(encoding=3, text=current_track.disk_number)
                mp3file["TSRC"] = TSRC(encoding=3, text=current_track.isrc)
                mp3file["TCON"] = TCON(encoding=3, text=genre)
                mp3file.tags.add(
                    APIC(
                        encoding=3, # 3 is for utf-8
                        mime='image/jpeg', # image/jpeg or image/png
                        type=3, # 3 is for the cover image
                        desc='Cover',
                        data=album_art
                    )
                )
                mp3file.save()
            if is_single_track:
                #global track_or_album_error
                return True
        except:
            if is_single_track:
                #global track_or_album_error
                return False


    def album_download(self, current_music_quality, real_album):
        album_art = requests.get(real_album.picture_url, stream=True).content
        try:
            for track in real_album.songs:
                self.track_download(current_music_quality, track, False, album_art)
            del album_art
            return True
        except:
            del album_art
            #global track_or_album_error
            return False

    def threading_download(self, current_music_quality, send_end):
        if track:
            if self.track_download(current_music_quality, track, True, requests.get(track.album.picture_url, stream=True).content):
                not_error = True
            else:
                not_error = False
            media = track.guess_file_name()
            media_type = "Song"
            send_end.send([not_error, media, media_type])
            GLib.idle_add(self.threading_download_callback,not_error, track.guess_file_name(), "Song")
        elif real_album:
            if self.album_download(current_music_quality, real_album):
                not_error = True
            else:
                not_error = False
            media = real_album.title
            media_type = "Album"
            send_end.send([not_error, media, media_type])
            GLib.idle_add(self.threading_download_callback,not_error, real_album.title, "Album")


    @Gtk.Template.Callback()
    def download_item_button_clicked(self, button):
        self.stop_item_revealer.set_reveal_child(True)
        self.start_download_spinner()
        self.download_item_button.set_sensitive(False)
        self.preferences_button.set_sensitive(False)
        if self.FLAC.get_active():
            current_music_quality = self.music_quality[0]
        elif self.MP3_320.get_active():
            current_music_quality = self.music_quality[1]
        else:
            current_music_quality = self.music_quality[2]
        recv_end, send_end = Pipe()
        global multiprocess_download
        multiprocess_download = Process(target=self.threading_download, args=(current_music_quality, send_end,))
        multiprocess_download.start()
        multiprocess_download_callback_callback_thread = threading.Thread(target=self.multiprocess_download_callback_callback, args=(recv_end,))
        multiprocess_download_callback_callback_thread.start()

    def multiprocess_download_callback_callback(self, recv_end):
        try:
            notification_data = recv_end.recv()
            GLib.idle_add(self.threading_download_callback,notification_data[0], notification_data[1], notification_data[2])
        except:
            pass

    @Gtk.Template.Callback()
    def deezer_find_activate(self, entry):
        self.download_clicked(self.download_button)

    @Gtk.Template.Callback()
    def password_activate(self, entry):
        self.login_clicked(self.login_button)

    def on_image_loaded(self, source, async_res, user_data):
        pixbuf = GdkPixbuf.Pixbuf.new_from_stream_finish(async_res)
        self.picture.set_from_pixbuf(pixbuf)

    def hide_download_spinner(self):
        self.download_button.set_sensitive(True)
        self.albums_or_tracks_revealer.set_reveal_child(False)
        self.deezer_link.set_sensitive(True)
        self.deezer_find_spinner_revealer.set_reveal_child(False)
        self.deezer_find_spinner.props_active = False
        self.deezer_find_spinner.stop()

    def find_track(self):
        # make gtk box fill
        self.details_box.set_halign(Gtk.Align.FILL)
        self.track_name.set_text(track.guess_file_name()+" (Track)")
        self.download_item_button.set_visible(True)
        self.picture.set_visible(True)
        self.found_track.set_reveal_child(True)
        self.hide_download_spinner()

    def find_album(self, album_songs):
        # make gtk box fill
        self.details_box.set_halign(Gtk.Align.FILL)
        self.track_name.set_text(real_album.title+" (Album)")
        for track in album_songs:
            new = Gtk.Label()
            new.set_visible(True)
            new.set_text(track)
            self.albums_or_tracks.add(new)
        self.download_item_button.set_visible(True)
        self.picture.set_visible(True)
        self.found_track.set_reveal_child(True)
        self.hide_download_spinner()

    def find_artist(self):
        # make gtk box fill
        self.details_box.set_halign(Gtk.Align.FILL)
        self.track_name.set_text(artist.name+" (Artist)")
        self.download_item_button.set_visible(False)
        self.picture.set_visible(True)
        self.found_track.set_reveal_child(True)
        self.hide_download_spinner()

    def error_link(self):
        # make gtk box centered
        self.details_box.set_halign(Gtk.Align.CENTER)
        self.track_name.set_text("Incorrect link!")
        self.download_item_button.set_visible(False)
        self.picture.set_visible(False)
        self.found_track.set_reveal_child(True)
        self.hide_download_spinner()

    def threading_find(self, link):
        try:
            if  link.rsplit('/', 2)[1] == "artist":
                global artist
                artist = link_deezer.get_artist(int(link.rsplit('/', 2)[2]))
                artist_picture = Gio.File.new_for_uri(artist.picture_url)
                GdkPixbuf.Pixbuf.new_from_stream_at_scale_async(artist_picture.read(cancellable=None),
                                        100, 100, # width and height
                                        True,    # preserve_aspect_ratio
                                        None,     # cancellable
                                        self.on_image_loaded, # callback,
                                        None)     # user_data
                GLib.idle_add(self.find_artist)
            elif link.rsplit('/', 2)[1] == "album":
                global real_album
                real_album = link_deezer.get_album(int(link.rsplit('/', 2)[2]))
                album_songs = []
                for album_track in real_album.songs:
                    album_songs.append(album_track.title)
                album_cover = Gio.File.new_for_uri(real_album.picture_thumbnail_url)
                GdkPixbuf.Pixbuf.new_from_stream_at_scale_async(album_cover.read(cancellable=None),
                                        100, 100, # width and height
                                        True,    # preserve_aspect_ratio
                                        None,     # cancellable
                                        self.on_image_loaded, # callback,
                                        None)     # user_data
                GLib.idle_add(self.find_album, album_songs)
            elif link.rsplit('/', 2)[1] == "track":
                global track
                track = link_deezer.get_track(int(link.rsplit('/', 2)[2]))
                album_cover = Gio.File.new_for_uri(track.album.picture_thumbnail_url)
                GdkPixbuf.Pixbuf.new_from_stream_at_scale_async(album_cover.read(cancellable=None),
                                        100, 100, # width and height
                                        True,    # preserve_aspect_ratio
                                        None,     # cancellable
                                        self.on_image_loaded, # callback,
                                        None)     # user_data
                GLib.idle_add(self.find_track)
            else:
                GLib.idle_add(self.error_link)
        except:
            GLib.idle_add(self.error_link)

    def start_download_spinner(self):
        self.download_button.set_sensitive(False)
        self.deezer_link.set_sensitive(False)
        self.deezer_find_spinner_revealer.set_reveal_child(True)
        self.deezer_find_spinner.props_active = True
        self.deezer_find_spinner.start()

    @Gtk.Template.Callback()
    def download_clicked(self, button):
        for child in self.albums_or_tracks.get_children():
            self.albums_or_tracks.remove(child)
        global real_album
        global artist
        global track
        real_album = None
        artist = None
        track = None
        self.start_download_spinner()
        self.found_track.set_reveal_child(False)
        thread_find = threading.Thread(target=self.threading_find, args=(self.deezer_link.get_text(),))
        thread_find.start()

    @Gtk.Template.Callback()
    def login_clicked(self, button):
        self.login_button.set_sensitive(False)
        self.password.set_sensitive(False)
        self.spinner_revealer.set_reveal_child(True)
        self.login_spinner.props_active = True
        self.login_spinner.start()
        global link_deezer
        link_deezer = Deezer()
        global search_deezer
        search_deezer = Client()
        thread_login = threading.Thread(target=self.threading_login, args=(self.password.get_text(),))
        thread_login.start()

    def threading_login(self, cookie):
        try:
            global user
            user = link_deezer.login_with_cookie(cookie)
            self.demixed_schemas.set_string("deezercookie",cookie)
            GLib.idle_add(self.move_stack_login_successful,)
        except:
            GLib.idle_add(self.login_error)

    def move_stack_login_successful(self):
        self.stack.set_visible_child(self.downloadpage)
        self.search_or_link_switcher.set_sensitive(True)
        self.info_popover_revealer.set_reveal_child(True)
        self.preferences_revealer.set_reveal_child(True)
        self.spinner_revealer.set_reveal_child(False)
        self.login_spinner.props_active = False
        self.login_spinner.stop()

    def login_error(self):
        self.login_button.set_sensitive(True)
        self.password.set_sensitive(True)
        self.error_login.set_markup("<span foreground=\"red\">Couldn't log in. Wrong email or cookie. Is your VPN off?</span>")
        self.error_reveal.set_reveal_child(True)
        self.spinner_revealer.set_reveal_child(False)
        self.login_spinner.props_active = False
        self.login_spinner.stop()
